package com.coolw.dynamic.thread.pool.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.coolw.common.api.PageResponse;
import com.coolw.dynamic.thread.pool.context.DynamicThreadPoolManager;
import com.coolw.dynamic.thread.pool.context.ThreadPoolProperties;
import com.coolw.dynamic.thread.pool.domain.request.ThreadPoolConfigAddRequest;
import com.coolw.dynamic.thread.pool.domain.request.ThreadPoolConfigPageRequest;
import com.coolw.dynamic.thread.pool.domain.request.ThreadPoolConfigUpdateRequest;
import com.coolw.dynamic.thread.pool.domain.response.ThreadPoolConfigResponse;
import com.coolw.dynamic.thread.pool.entity.ThreadPoolConfigEntity;
import com.coolw.dynamic.thread.pool.mapper.ThreadPoolConfigMapper;
import com.coolw.dynamic.thread.pool.service.ThreadPoolConfigHisService;
import com.coolw.dynamic.thread.pool.service.ThreadPoolConfigService;
import com.coolw.dynamic.thread.pool.util.MybatisPlusUtil;
import lombok.extern.slf4j.Slf4j;
import org.dromara.hutool.core.bean.BeanUtil;
import org.dromara.hutool.core.data.id.IdUtil;
import org.dromara.hutool.core.lang.Assert;
import org.dromara.hutool.core.text.StrUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.Objects;


/**
 * 线程池配置Service实现类
 *
 * @author coolw
 * @date 2024/5/15 14:38
 */
@Slf4j
@Service
public class ThreadPoolConfigServiceImpl extends ServiceImpl<ThreadPoolConfigMapper, ThreadPoolConfigEntity>
        implements ThreadPoolConfigService {

    @Autowired
    private ThreadPoolConfigHisService threadPoolConfigHisService;

    @Override
    public PageResponse<ThreadPoolConfigResponse> pageList(ThreadPoolConfigPageRequest request) {
        // 分页参数
        LambdaQueryWrapper<ThreadPoolConfigEntity> queryWrapper = Wrappers.<ThreadPoolConfigEntity>lambdaQuery()
                .eq(StrUtil.isNotBlank(request.getPoolNo()), ThreadPoolConfigEntity::getPoolNo, request.getPoolNo())
                .eq(StrUtil.isNotBlank(request.getPoolCode()), ThreadPoolConfigEntity::getPoolCode, request.getPoolCode())
                .eq(StrUtil.isNotBlank(request.getPoolName()), ThreadPoolConfigEntity::getPoolName, request.getPoolName())
                .orderByDesc(ThreadPoolConfigEntity::getId);
        Page<ThreadPoolConfigEntity> page = Page.of(request.getPageNum(), request.getPageSize());

        // 分页查询
        Page<ThreadPoolConfigEntity> pageSource = page(page, queryWrapper);

        // 分页结果
        return MybatisPlusUtil.toPage(pageSource, ThreadPoolConfigResponse.class);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean save(ThreadPoolConfigAddRequest request) {
        checkThreadQuantity(null, request.getCorePoolSize(), request.getMaxPoolSize());

        ThreadPoolConfigEntity configEntity = BeanUtil.copyProperties(request, ThreadPoolConfigEntity.class);
        configEntity.setPoolNo(IdUtil.getSnowflakeNextIdStr());
        boolean result = save(configEntity);
        Assert.isTrue(result, "线程池配置保存失败");

        // 创建线程池
        ThreadPoolProperties properties = buildThreadPoolProperties(configEntity);
        boolean create = DynamicThreadPoolManager.getInstance().createThreadPool(properties);
        Assert.isTrue(create, "创建线程池失败");

        return result;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean update(ThreadPoolConfigUpdateRequest request) {
        ThreadPoolConfigEntity currentConfig = getById(request.getId());
        Assert.notNull(currentConfig, "线程池配置信息不存在");

        checkThreadQuantity(currentConfig, request.getCorePoolSize(), request.getMaxPoolSize());

        // 修改线程池配置
        ThreadPoolConfigEntity configEntity = BeanUtil.copyProperties(request, ThreadPoolConfigEntity.class);
        boolean result = updateById(configEntity);
        Assert.isTrue(result, "线程池配置更新失败");

        // 修改运行中的线程池参数
        ThreadPoolProperties properties = buildThreadPoolProperties(configEntity);
        boolean modify = DynamicThreadPoolManager.getInstance().modifyThreadPool(properties);
        Assert.isTrue(modify, "线程池参数修改失败");

        // 保存线程池配置变更记录
        threadPoolConfigHisService.saveConfigHis(currentConfig);

        return result;
    }

    @Override
    public List<ThreadPoolConfigEntity> listByPoolCodes(List<String> poolCodes) {
        Assert.notEmpty(poolCodes, "poolCodes 不能为空");

        LambdaQueryWrapper<ThreadPoolConfigEntity> queryWrapper = Wrappers.<ThreadPoolConfigEntity>lambdaQuery()
                .in(ThreadPoolConfigEntity::getPoolCode, poolCodes)
                .orderByDesc(ThreadPoolConfigEntity::getId);
        return list(queryWrapper);
    }

    @Override
    public ThreadPoolConfigEntity getOneByPoolCode(String poolCode) {
        Assert.notBlank(poolCode, "poolCode 不能为空");

        LambdaQueryWrapper<ThreadPoolConfigEntity> queryWrapper = Wrappers.<ThreadPoolConfigEntity>lambdaQuery()
                .eq(ThreadPoolConfigEntity::getPoolCode, poolCode);
        return getOne(queryWrapper);
    }

    /**
     * 检查核心、最大线程数量是否合法
     *
     * @param config       当前线程池配置信息
     * @param corePoolSize 核心线程数
     * @param maxPoolSize  最大线程数
     */
    private void checkThreadQuantity(ThreadPoolConfigEntity config, int corePoolSize, int maxPoolSize) {
        Assert.isTrue(maxPoolSize >= corePoolSize, "核心线程数不能大于最大线程数");
        if (Objects.nonNull(config)) {
            Assert.isTrue(config.getMaxPoolSize() >= corePoolSize, "核心线程数不能大于最大线程数");
            Assert.isTrue(maxPoolSize >= config.getCorePoolSize(), "最大线程数不能小于核心线程数");
        }
    }

    private ThreadPoolProperties buildThreadPoolProperties(ThreadPoolConfigEntity config) {
        ThreadPoolProperties properties = new ThreadPoolProperties();
        properties.setPoolCode(config.getPoolCode());
        properties.setCorePoolSize(config.getCorePoolSize());
        properties.setMaxPoolSize(config.getMaxPoolSize());
        properties.setKeepAliveTime(config.getKeepAliveTime());
        properties.setKeepAliveTimeUnit(config.getKeepAliveTimeUnit());
        properties.setQueueType(config.getQueueType());
        properties.setQueueSize(config.getQueueSize());
        properties.setRejectStrategy(config.getRejectStrategy());
        return properties;
    }
}
